/*
 * @Author: shenghao.xu
 * @Date: 2023-04-11 18:31:07
 * @LastEditors: shenghao.xu
 * @LastEditTime: 2023-04-21 10:18:32
 * @Description:
 * email:545403892@qq.com
 * Copyright (c) 2023 by shenghao.xu, All Rights Reserved.
 */

#include "delay.h"
static uint16_t g_fac_ms = 0; // ms延时倍乘数,在os下,代表每个节拍的ms数
static uint32_t g_fac_us = 0; /* us延时倍乘数 */

/* 如果SYS_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS) */
#if SYS_SUPPORT_OS

/* 添加公共头文件 ( ucos需要用到) */
#include "FreeRTOS.h"
#include "task.h"

extern void xPortSysTickHandler(void);

/**
 * @brief     systick中断服务函数,使用OS时用到
 * @param     ticks : 延时的节拍数
 * @retval    无
 */
void SysTick_Handler(void)
{
    HAL_IncTick();
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) /* OS开始跑了,才执行正常的调度处理 */
    {
        xPortSysTickHandler();
    }
}
#endif

/**
 * @brief     初始化延迟函数
 * @param     sysclk: 系统时钟频率, 即CPU频率(rcc_c_ck), 168Mhz
 * @retval    无
 */
void delay_init(uint16_t sysclk)
{
#if SYS_SUPPORT_OS /* 如果需要支持OS */
    uint32_t reload;
#endif
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); /* SYSTICK使用内核时钟源,同CPU同频率 */
    g_fac_us = sysclk;                                   /* 不论是否使用OS,g_fac_us都需要使用 */
    g_fac_ms = g_fac_ms;
#if SYS_SUPPORT_OS                             /* 如果需要支持OS. */
    reload = sysclk;                           /* 每秒钟的计数次数 单位为M */
    reload *= 1000000 / configTICK_RATE_HZ;    /* 根据delay_ostickspersec设定溢出时间,reload为24位
                                                * 寄存器,最大值:16777216,在168M下,约合0.099s左右
                                                */
    g_fac_ms = 1000 / configTICK_RATE_HZ;      // 代表OS可以延时的最少单位
    SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; /* 开启SYSTICK中断 */
    SysTick->LOAD = reload;                    /* 每1/delay_ostickspersec秒中断一次 */
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;  /* 开启SYSTICK */
#endif
}

#if SYS_SUPPORT_OS /* 如果需要支持OS, 用以下代码 */

// 延时nus
// nus:要延时的us数.
// nus:0~204522252(最大值即2^32/g_fac_us@g_fac_us=168)
void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD; // LOAD的值
    ticks = nus * g_fac_us;          // 需要的节拍数
    told = SysTick->VAL;             // 刚进入时的计数器值
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
                tcnt += told - tnow; // 这里注意一下SYSTICK是一个递减的计数器就可以了.
            else
                tcnt += reload - tnow + told;
            told = tnow;
            if (tcnt >= ticks)
                break; // 时间超过/等于要延迟的时间,则退出.
        }
    };
}
// 延时nms
// nms:要延时的ms数
// nms:0~65535
void delay_ms(uint16_t nms)
{
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) // 系统已经运行
    {
        if (nms >= g_fac_ms) // 延时的时间大于OS的最少时间周期
        {
            vTaskDelay(nms / g_fac_ms); // FreeRTOS延时
        }
        else
        {
            nms %= g_fac_ms;                  // OS已经无法提供这么小的延时了,采用普通方式延时
            delay_us((uint32_t)(nms * 1000)); // 普通方式延时
        }
    }
    else
    {
        delay_us((uint32_t)(nms * 1000)); // 普通方式延时
    }
}

// 延时nms,不会引起任务调度
// nms:要延时的ms数
void delay_xms(uint32_t nms)
{
    uint32_t i;
    for (i = 0; i < nms; i++)
        delay_us(1000);
}

#else /* 不使用OS时, 用以下代码 */

/**
 * @brief       延时nus
 * @param       nus: 要延时的us数.
 * @note        注意: nus的值,不要大于34952us(最大值即2^24 / g_fac_us @g_fac_us = 168)
 * @retval      无
 */
void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD; /* LOAD的值 */
    ticks = nus * g_fac_us;          /* 需要的节拍数 */
    told = SysTick->VAL;             /* 刚进入时的计数器值 */
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow; /* 这里注意一下SYSTICK是一个递减的计数器就可以了 */
            }
            else
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                __NOP();
                break; /* 时间超过/等于要延迟的时间,则退出 */
            }
        }
    }
}

/**
 * @brief       延时nms
 * @param       nms: 要延时的ms数 (0< nms <= 65535)
 * @retval      无
 */
void delay_ms(uint16_t nms)
{
    uint32_t repeat = nms / 30; /*  这里用30,是考虑到可能有超频应用 */
    uint32_t remain = nms % 30;

    while (repeat)
    {
        delay_us(30 * 1000); /* 利用delay_us 实现 1000ms 延时 */
        repeat--;
    }

    if (remain)
    {
        delay_us(remain * 1000); /* 利用delay_us, 把尾数延时(remain ms)给做了 */
    }
}

#endif
